﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 串口联调五插针
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Load += Form1_Load;
            btnOpenOrClose.Click += btnOpenOrClose_Click;
            btnOneTimeRead.Click += btnOneTimeRead_Click;
            btnRealTimeRead.Click += btnRealTimeRead_Click;
            btnUpdate.Click += btnUpdate_Click;
            serialPort1.DataReceived += serialPort1_DataReceived;

            ushort a = 255;
            byte[] bytes = BitConverter.GetBytes(a);
        }

        List<int> hsl = new List<int>();
        List<int> wdz = new List<int>();
        List<int> ddl = new List<int>();
        List<int> phz = new List<int>();
        private void Form1_Load(object sender, EventArgs e)
        {
            BindSerialParameter();
            BindChart();
        }

        private void BindChart()
        {
            chart1.Series[0].Points.DataBindY(hsl);
            chart1.Series[1].Points.DataBindY(wdz);
            chart1.Series[2].Points.DataBindY(ddl);
            chart1.Series[3].Points.DataBindY(phz);
        }

        #region 绑定串口相关参数
        private void BindSerialParameter()
        {
            BindPortName();
            BindBaudRate();
            BindDataBits();
            BindStopBits();
            BindParity();
        }
        private void BindPortName()
        {
            string[] portNames = SerialPort.GetPortNames();
            cbbPortName.Items.Clear();
            foreach (string portName in portNames)
            {
                cbbPortName.Items.Add(portName);
            }
            if (cbbPortName.Items.Count > 0) cbbPortName.SelectedIndex = 0;
        }
        private void BindBaudRate()
        {
            int[] baudRates = new int[] { 9600, 19200, 38400, 57600, 115200, 128000, 256000 };
            cbbBaudRate.Items.Clear();
            foreach (int baudRate in baudRates)
            {
                cbbBaudRate.Items.Add(baudRate);
            }
            if (cbbBaudRate.Items.Count > 0) cbbBaudRate.SelectedIndex = 0;
        }
        private void BindDataBits()
        {
            int[] dataBits = new int[] { 8, 7 };
            cbbDataBits.Items.Clear();
            foreach (int baudRate in dataBits)
            {
                cbbDataBits.Items.Add(baudRate);
            }
            if (cbbDataBits.Items.Count > 0) cbbDataBits.SelectedIndex = 0;
        }
        private void BindStopBits()
        {
            StopBits[] stopBits = new StopBits[] { StopBits.One, StopBits.Two };
            cbbStopBits.Items.Clear();
            foreach (StopBits stopBit in stopBits)
            {
                cbbStopBits.Items.Add(Convert.ToInt32(stopBit));
            }
            cbbStopBits.SelectedIndex = 0;
        }

        private void BindParity()
        {
            string[] parities = Enum.GetNames(typeof(Parity));
            cbbParity.Items.Clear();
            foreach (string parity in parities)
            {
                cbbParity.Items.Add(parity);
            }
            cbbParity.SelectedIndex = 0;
        }
        #endregion

        private void btnOpenOrClose_Click(object sender, EventArgs e)
        {
            try
            {
                if (btnOpenOrClose.Text == "连接")
                {
                    serialPort1.PortName = cbbPortName.SelectedItem.ToString();
                    serialPort1.BaudRate = int.Parse(cbbBaudRate.SelectedItem.ToString());
                    serialPort1.DataBits = int.Parse(cbbDataBits.SelectedItem.ToString());
                    serialPort1.StopBits = (StopBits)Enum.Parse(typeof(StopBits), cbbStopBits.SelectedItem.ToString());
                    serialPort1.Parity = (Parity)Enum.Parse(typeof(Parity), cbbParity.SelectedItem.ToString());

                    serialPort1.Open();

                    btnOpenOrClose.Text = "关闭";
                }
                else
                {
                    serialPort1.Close();

                    btnOpenOrClose.Text = "连接";
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
        }

        private async void btnOneTimeRead_Click(object sender, EventArgs e)
        {
            // 三种写法：16进制写法，10进制的写法，取高低位公式
            byte[] buffer = new byte[8];
            buffer[0] = 0x01; // 设备地址   buffer[0]=0X01;  buffer[0]=1;
            buffer[1] = 0x03; // 功能码  0x03读取，0x06写入  0x10批量写入  buffer[1] = 3;

            buffer[2] = 0x00;  // 起始地址高位  buffer[2] = 0;
            buffer[3] = 0x00;  // 起始地址低位  buffer[3] = 0;

            buffer[4] = 0X00; // 数据长度高位   buffer[5] = (byte)(4 / 0XFF);
            buffer[5] = 0X04; // 数据长度低位   buffer[5] = (byte)(4 % 0XFF);

            byte[] crc = CRC16(buffer);
            buffer[6] = crc[0];
            buffer[7] = crc[1];
            serialPort1.Write(buffer, 0, buffer.Length);

            await Task.Delay(500);

            byte[] buffer1 = new byte[8];
            buffer1[0] = 0x01; // 设备地址   buffer[0]=0X01;  buffer[0]=1;
            buffer1[1] = 0x03; // 功能码  0x03读取，0x06写入  0x10批量写入  buffer[1] = 3;

            buffer1[2] = 0X07;  // 起始地址高位  buffer[2] = 0;
            buffer1[3] = 0XD0;  // 起始地址低位  buffer[3] = 0;

            buffer1[4] = 0X00; // 数据长度高位   buffer[5] = (byte)(4 / 0XFF);
            buffer1[5] = 0X02; // 数据长度低位   buffer[5] = (byte)(4 % 0XFF);

            byte[] crc1 = CRC16(buffer1);
            buffer1[6] = crc1[0];
            buffer1[7] = crc1[1];

            serialPort1.Write(buffer1, 0, buffer1.Length);
        }

        private async void Send()
        {
            // 三种写法：16进制写法，10进制的写法，取高低位公式
            byte[] buffer = new byte[8];
            buffer[0] = 0x01; // 设备地址   buffer[0]=0X01;  buffer[0]=1;
            buffer[1] = 0x03; // 功能码  0x03读取，0x06写入  0x10批量写入  buffer[1] = 3;

            buffer[2] = 0x00;  // 起始地址高位  buffer[2] = 0;
            buffer[3] = 0x00;  // 起始地址低位  buffer[3] = 0;

            buffer[4] = 0X00; // 数据长度高位   buffer[5] = (byte)(4 / 0XFF);
            buffer[5] = 0X04; // 数据长度低位   buffer[5] = (byte)(4 % 0XFF);

            byte[] crc = CRC16(buffer);
            buffer[6] = crc[0];
            buffer[7] = crc[1];
            serialPort1.Write(buffer, 0, buffer.Length);

            await Task.Delay(500);

            byte[] buffer1 = new byte[8];
            buffer1[0] = 0x01; // 设备地址   buffer[0]=0X01;  buffer[0]=1;
            buffer1[1] = 0x03; // 功能码  0x03读取，0x06写入  0x10批量写入  buffer[1] = 3;

            buffer1[2] = 0X07;  // 起始地址高位  buffer[2] = 0;
            buffer1[3] = 0XD0;  // 起始地址低位  buffer[3] = 0;

            buffer1[4] = 0X00; // 数据长度高位   buffer[5] = (byte)(4 / 0XFF);
            buffer1[5] = 0X02; // 数据长度低位   buffer[5] = (byte)(4 % 0XFF);

            byte[] crc1 = CRC16(buffer1);
            buffer1[6] = crc1[0];
            buffer1[7] = crc1[1];

            serialPort1.Write(buffer1, 0, buffer1.Length);
        }

        /// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="slaveAddress">设备地址（从站地址）</param>
        /// <param name="functionCode">功能码 3读取，6写入</param>
        /// <param name="startAddress">起始地址</param>
        /// <param name="quantity">读取长度</param>
        private void Send(ushort slaveAddress, ushort functionCode, ushort startAddress, ushort quantity)
        {
            byte[] buffer = new byte[8];
            buffer[0] = (byte)slaveAddress;
            buffer[1] = (byte)functionCode;

            


           

            buffer[2] = (byte)(startAddress / 0XFF);
            buffer[3] = (byte)(startAddress % 0XFF);

            buffer[4] = (byte)(quantity / 256);
            buffer[5] = (byte)(quantity % 256);

            byte[] bytes = BitConverter.GetBytes(slaveAddress);
            buffer[6] = bytes[0];
            buffer[7] = bytes[1];
            serialPort1.Write(buffer, 0, buffer.Length);
        }


        // data的长度8, -2得到真实的数据长度：6位
        public static byte[] CRC16(byte[] data)
        {
            int crc = 0xffff;  // ushort的最大值65535
            for (int i = 0; i < data.Length - 2; i++)
            {
                crc = crc ^ data[i];
                for (int j = 0; j < 8; j++)
                {
                    int temp = 0;
                    temp = crc & 1;
                    crc = crc >> 1;
                    crc = crc & 0x7fff;
                    if (temp == 1)
                    {
                        crc = crc ^ 0xa001;
                    }
                    crc = crc & 0xffff;
                }
            }
            //CRC寄存器高低位互换
            byte[] crc16 = new byte[2];
            crc16[1] = (byte)((crc >> 8) & 0xff);
            crc16[0] = (byte)(crc & 0xff);
            return crc16;
        }

        // 取消标识资源，对象可以重新生成token标识，也可以取消任务
        CancellationTokenSource cts;
        private void btnRealTimeRead_Click(object sender, EventArgs e)
        {
            if (btnRealTimeRead.Text == "实时采集")
            {
                cts = new CancellationTokenSource();
                Task.Run(async () =>
                {
                    while (cts.IsCancellationRequested == false)
                    {
                        Send();

                        // 发送报文，有个间隔时间，不要发太快。
                        await Task.Delay(3000);
                    }

                }, cts.Token);

                btnRealTimeRead.Text = "停止采集";
            }
            else
            {
                // 取消任务
                cts.Cancel();

                txtHSL.Text = "";
                txtWDZ.Text = "";
                txtDDL.Text = "";
                txtPHZ.Text = "";

                txtSBDZ.Text = "";
                txtBTL.Text = "";

                btnRealTimeRead.Text = "实时采集";
            }
        }

        private void btnUpdate_Click(object sender, EventArgs e)
        {
            byte[] buffer1 = new byte[8];
            buffer1[0] = 0x01; // 设备地址   buffer[0]=0X01;  buffer[0]=1;
            buffer1[1] = 0x06; // 功能码  0x03读取，0x06写入  0x10批量写入  buffer[1] = 3;

            buffer1[2] = 0X07;  // 起始地址高位  buffer[2] = 0;
            buffer1[3] = 0XD1;  // 起始地址低位  buffer[3] = 0;

            int data = Convert.ToInt32(txtBTL.Text);  // 不要超过256
            buffer1[4] = (byte)(data / 0XFF); // 数据长度高位   buffer[5] = (byte)(4 / 0XFF);
            buffer1[5] = (byte)(data % 0XFF); // 数据长度低位   buffer[5] = (byte)(4 % 0XFF);

            byte[] crc1 = CRC16(buffer1);
            buffer1[6] = crc1[0];
            buffer1[7] = crc1[1];

            serialPort1.Write(buffer1, 0, buffer1.Length);
        }

        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] buffer = new byte[serialPort1.BytesToRead];
            int count = serialPort1.Read(buffer, 0, buffer.Length);

            if (count == 13)
            {
                // 跨线程更新主线程中的资源（控件），说明DataReceived方法中的逻辑是在子线程（分线程）中执行的。
                Invoke(new Action(() =>
                {
                    // 公式：高位*256 + 低位
                    // 含水率，温度值，电导率，PH值
                    txtHSL.Text = (buffer[3] * 256 + buffer[4]).ToString();
                    txtWDZ.Text = (buffer[5] * 256 + buffer[6]).ToString();
                    txtDDL.Text = (buffer[7] * 256 + buffer[8]).ToString();
                    txtPHZ.Text = (buffer[9] * 256 + buffer[10]).ToString();

                    if (hsl.Count > 10)
                    {
                        hsl.RemoveAt(0);
                        wdz.RemoveAt(0);
                        ddl.RemoveAt(0);
                        phz.RemoveAt(0);
                    }
                    hsl.Add(Convert.ToInt32(txtHSL.Text));
                    wdz.Add(Convert.ToInt32(txtWDZ.Text));
                    ddl.Add(Convert.ToInt32(txtDDL.Text));
                    phz.Add(Convert.ToInt32(txtPHZ.Text));
                    BindChart();
                }));
            }

            if (count == 9)
            {
                Invoke(new Action(() =>
                {
                    //// 设备地址，波特率
                    //txtSBDZ.Text = (buffer[3] * 256 + buffer[4]).ToString();
                    //txtBTL.Text = (buffer[5] * 256 + buffer[6]).ToString();


                    byte[] bytes = new byte[] { buffer[4], buffer[3] };
                    txtSBDZ.Text = BitConverter.ToInt16(bytes, 0).ToString();
                    byte[] bytes1 = new byte[] { buffer[6], buffer[5] };
                    txtBTL.Text = BitConverter.ToInt16(bytes1, 0).ToString();

                }));
            }

        }
    }
}
